﻿// BookShelf.Model.cs
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.ServiceModel.DomainServices.Client;
using System.Windows;
using BookClub.Core;
using BookClub.Web.DataModel;
using BookClub.Web.Services;

namespace BookClub.Pages {

    public class BookShelfModel : ViewModelBase {

        private ReferenceDataContext _referenceData;
        private BookShelfContext _bookShelfContext;

        private EntityQuery<Book> _bookQuery;
        private int _pageIndex;
        private OperationBase _currentOperation;

        private Book _selectedBook;
        private Book _previousBook;

        public BookShelfModel() {
            _referenceData = new ReferenceDataContext();
            _referenceData.Load(_referenceData.GetCategoriesQuery(), /* throwOnError */ false);

            _bookShelfContext = new BookShelfContext();
            _bookShelfContext.AddReference(typeof(Category), _referenceData);
            _bookShelfContext.PropertyChanged += OnBookShelfContextPropertyChanged;
        }

        public bool CanLoadMoreBooks {
            get {
                return _bookShelfContext.Books.Any();
            }
        }

        public IEnumerable<Category> Categories {
            get {
                return _referenceData.Categories;
            }
        }

        public IEnumerable<Book> Books {
            get {
                return _bookShelfContext.Books;
            }
        }

        public Book SelectedBook {
            get {
                return _selectedBook;
            }
            set {
                if ((_selectedBook != null) && IsEditing) {
                    CancelEditing();
                }
                _previousBook = _selectedBook;
                _selectedBook = value;
                RaisePropertyChanged("SelectedBook");

                if ((_selectedBook != null) &&
                    (_selectedBook.HasValidationErrors || _selectedBook.IsNew)) {
                    EditBook();
                }
            }
        }

        public OperationBase CurrentOperation {
            get {
                return _currentOperation;
            }
            set {
                _currentOperation = value;
                RaisePropertyChanged("CurrentOperation");
            }
        }

        public void LoadBooks(string searchText) {
            _pageIndex = 0;

            EntityQuery<Book> bookQuery = _bookShelfContext.GetBooksQuery();
            if (String.IsNullOrEmpty(searchText) == false) {
                bookQuery = bookQuery.Where(b => b.Title.Contains(searchText) ||
                                                 b.Author.Contains(searchText) ||
                                                 b.Description.Contains(searchText));
            }

            _bookQuery = bookQuery;

            EntityQuery<Book> pagedQuery = bookQuery.Skip(_pageIndex * 5).Take(5);

            _bookShelfContext.Books.Clear();
            RaisePropertyChanged("CanLoadMoreBooks");

            CurrentOperation = _bookShelfContext.Load(pagedQuery, delegate(LoadOperation<Book> bookLoadOperation) {
                RaisePropertyChanged("CanLoadMoreBooks");
            }, null);
        }

        public void LoadMoreBooks() {
            _pageIndex++;

            EntityQuery<Book> pagedQuery = _bookQuery.Skip(_pageIndex * 5).Take(5);
            CurrentOperation = _bookShelfContext.Load(pagedQuery);
        }


        private bool _isEditing;

        public bool IsEditing {
            get {
                return _isEditing;
            }
            private set {
                _isEditing = value;
                RaisePropertyChanged("IsEditing");
            }
        }

        public void EditBook() {
            ((IEditableObject)SelectedBook).BeginEdit();
            IsEditing = true;
        }

        public void CancelEditing() {
            if (IsEditing == false) {
                return;
            }

            IsEditing = false;

            ((IEditableObject)SelectedBook).CancelEdit();
            if (SelectedBook.IsNew) {
                SelectedBook = _previousBook;
            }
        }

        public void CommitEditing() {
            if (IsEditing == false) {
                return;
            }

            if (SelectedBook.Validate(_bookShelfContext.ValidationContext) == false) {
                return;
            }

            if (SelectedBook.IsNew) {
                _bookShelfContext.BookExists(SelectedBook.ASIN,
                    delegate(InvokeOperation<bool> operation) {
                        if (operation.Value) {
                            ValidationResult duplicateError =
                                new ValidationResult("This book already exists in the book club.", new string[] { "Title" });
                            SelectedBook.ValidationErrors.Add(duplicateError);
                        }
                        else {
                            EndEditing();
                        }
                    }, null);
            }
            else {
                EndEditing();
            }
        }

        private void EndEditing() {
            ((IEditableObject)SelectedBook).EndEdit();

            if (SelectedBook.IsNew) {
                _bookShelfContext.Books.Add(SelectedBook);
            }

            IsEditing = false;
        }

        public void ShareBook() {
            Book book = new Book() {
                PublishDate = DateTime.UtcNow.Date,
                AddedDate = DateTime.UtcNow.Date
            };

            SelectedBook = book;

            EditBook();
        }

        public void UnshareBook() {
            Book book = SelectedBook;
            SelectedBook = null;
            _bookShelfContext.Books.Remove(book);
        }


        public bool HasChanges {
            get {
                return _bookShelfContext.HasChanges;
            }
        }

        public void SaveBooks() {
            CurrentOperation = _bookShelfContext.SubmitChanges(delegate(SubmitOperation operation) {
                if (operation.HasError) {
                    MessageBox.Show("There was an error saving one or more changes. Please check the books marked with errors.");
                    operation.MarkErrorAsHandled();
                }
            }, null);
        }

        private void OnBookShelfContextPropertyChanged(object sender, PropertyChangedEventArgs e) {
            if (String.CompareOrdinal(e.PropertyName, "HasChanges") == 0) {
                RaisePropertyChanged("HasChanges");
            }
        }
    }
}
